/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ucjava.chat.commserve.rmi.guiclient;

import com.ucjava.chat.admanager.AdGui;
import com.ucjava.chat.admanager.IAdvertising;
import com.ucjava.chat.commserve.rmi.common.ChatClientConstants;
import com.ucjava.chat.commserve.rmi.common.IChatClientRMI;
import com.ucjava.chat.commserve.rmi.common.IChatService;
import com.ucjava.chat.commserve.rmi.common.Message;
import com.ucjava.chat.commserve.rmi.common.OwnIP;
import com.ucjava.chat.commserve.rmi.common.PrefStore;
import com.ucjava.chat.commserve.rmi.common.PrivacySettings;
import com.ucjava.chat.commserve.rmi.common.ResultCodes;
import com.ucjava.chat.commserve.rmi.common.User;
import com.ucjava.chat.socialnetworking.userinterface.FriendsList;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;

/**
 *
 * @author tony
 */
public class MainForm extends javax.swing.JFrame implements WindowListener {

    // Hold user's preferences:
    private static PrefStore prefStore;
    // Our access to server:
    private IChatService serverStub;
    // Server's access to us:
    private IChatClientRMI clientStub;
    // Who are we? What's our status?
    private User loggedInAs;
    private boolean isLoggedIn;
    // Who's the server connected to?
    private String serverIP;
    private String loggedInStatusString;
    private String loggedOutStatusString;
    private IAdvertising advertisingObj;

    private void setStateToLoggedIn(String uname) {
        loggedInAs = new User();
        loggedInAs.setUserName(uname);
        isLoggedIn = true;
        lblTopStatus.setText(loggedInStatusString
                + " " + uname);
        //btnLogin.setText(prefStore.getFileLoginMenuText());
        btnLogin.setText(prefStore.getFileLogoutMenuText());
        btnSend.setVisible(true);
        this.btnBuyAds.setVisible(true);
        this.btnFriendsList.setVisible(true);

    }

    private void setStateToLoggedOut() {
        loggedInAs = null;
        isLoggedIn = false;
        this.lblTopStatus.setText(loggedOutStatusString);
        //btnLogin.setText(prefStore.getFileLogoutMenuText());
        btnLogin.setText(prefStore.getFileLoginMenuText());
        btnSend.setVisible(false);
        this.btnBuyAds.setVisible(false);
        this.btnFriendsList.setVisible(false);
    }

    /**
     * Initialize application properties
     */
    private static void initProperties() {


        OwnIP ownIP = null;
        try {
            ownIP = OwnIP.buildOwnIP();
            System.out.println("Our IP: " + ownIP.getIP());
        } catch (Exception ex) {
            Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Could not determine our IP!");
            System.exit(1);
        }
        // Set this property so RMI calls 
        // create client stub with proper
        // ip "return address."
        System.setProperty("java.rmi.server.hostname",
                ownIP.getIP());
    }

    /**
     * mutator for serverIP, so that an alternate server can be specified on the
     * command line when the program is started. In that case, main() will call
     * this mutator. This way, main() can remain a static method without
     * requiring serverIP to be static as well. We use a non-standard name for
     * this mutator to avoid confusion with PrefStore#setServerIP()
     */
    private void changeServerIP(String ip) {
        serverIP = ip;
    }

    /**
     * Creates new form MainForm
     */
    public MainForm(String newIP) {

        prefStore = PrefStore.makePrefStore();
        //prefStore = new PrefStore();
        loggedInAs = null;
        isLoggedIn = false;
        // Did user specify a server on command line
        // (contents of newIP variable)? If so,
        // use that; otherwise, use default:
        if (newIP != null) {
            serverIP = newIP;
        } else {
            serverIP = prefStore.getServerIP();
        }
        System.out.println("Server ip: " + serverIP);

        // Add all the labels, buttons, menus,
        // and text areas to this JFrame.
        buildGUI();

        // Add listener so that window closing
        // will be handled.
        this.addWindowListener(this);

        // 
        // Connect to server:
        try {
            this.findServer();
        } catch (NotBoundException ex) {
            JOptionPane.showMessageDialog(rootPane,
                    ex.getLocalizedMessage(),
                    prefStore.getCantContactServerMsg()
                    + " " + this.serverIP,
                    JOptionPane.ERROR_MESSAGE);
            //Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
            doExit(1);
        } catch (RemoteException ex) {
            JOptionPane.showMessageDialog(rootPane,
                    ex.getLocalizedMessage(),
                    prefStore.getCantContactServerMsg()
                    + " " + this.serverIP,
                    JOptionPane.ERROR_MESSAGE);
            //Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
            doExit(1);
        }
        // If findServer succeeded, we're connected, but not yet logged in.
        this.setStateToLoggedOut();
//        this.lblTopStatus.setText(loggedOutStatusString);
        
        //start the thread that updates the ads.  
        Thread t = new Thread(new AdUpdate());
        t.start();
    } // end MainForm() constructor

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    /**
     * initComponents() replaced by buildGUI()
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents()
    {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        // If user specified an alternate server
        // on the command line, use that. Otherwise,
        // set it to null. This value will be passed
        // to the MainForm() constructor.
        final String newIP = (args.length >= 1) ? args[0] : null;
        // Get local copy of config/pref object:
        final PrefStore prefStore = PrefStore.makePrefStore();
        if (newIP != null){
            //sets the server IP address, so that any class using RMI can use it
            prefStore.setServerIP(newIP);
        }
        else{
          prefStore.setServerIP("localhost");  
        }             
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                MainForm mainForm = new MainForm(newIP);
                /**
                 * Attempt to instantiate a client stub. If this fails, there
                 * will be a RemoteException thrown.
                 *
                 * NOTE: This should probably be moved out of main(), probably
                 * into MainForm's constructor.
                 */
                {
                    try {
                        mainForm.makeClientStub(clientDisplay);
                    } catch (RemoteException ex) {
                        JOptionPane.showMessageDialog(null,
                                ex.getLocalizedMessage(),
                                prefStore.getCantCreateClientMsg(),
                                JOptionPane.ERROR_MESSAGE);
                        //Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                        doExit(2);
                    }
                }
                mainForm.setVisible(true);
            } // end of run()
        });
        initProperties();

    }
    // These variables are initialized by GUI builder methods below:
    private javax.swing.JButton btnSend;
    private javax.swing.JButton btnBuyAds;
    private javax.swing.JButton btnLogin;
    private javax.swing.JButton btnFriendsList;
    private static com.ucjava.chat.commserve.rmi.guiclient.JListClientDisplay clientDisplay;
    private javax.swing.JPopupMenu.Separator jSeparator1;
    private javax.swing.JLabel lblAdDisplay;
    private javax.swing.JLabel lblSendHint;
    private javax.swing.JLabel lblTopStatus;
    private javax.swing.JMenuItem mniExit;
    private javax.swing.JMenuItem mniLogin;
    private javax.swing.JMenuItem mniLogout;
    private javax.swing.JMenuItem mniOptions;
    private javax.swing.JMenuBar mnuBarMain;
    private javax.swing.JMenu mnuEdit;
    private javax.swing.JMenu mnuFile;
    private javax.swing.JPanel pnlBottom;
    private javax.swing.JPanel pnlHeader;
    private javax.swing.JPanel pnlMain;
    private javax.swing.JPanel pnlSend;
    private javax.swing.JScrollPane scrPaneMain;
    private javax.swing.JTextField txtComposeMessage;

    // <editor-fold defaultstate="collapsed" desc="RMI Connection Code">
    private void findServer() throws NotBoundException, RemoteException {

        // locate the RMI registry
        Registry registry =
                LocateRegistry.getRegistry(this.serverIP);
        // ask the registry for a stub so we can call the
        // server
        serverStub =
                (IChatService) registry.lookup(ChatClientConstants.SERVICENAME);

        //IAdvertising adManager;
        advertisingObj = (IAdvertising) registry.lookup("PurchaseAdvertisements");

    }

    /**
     * Instantiate stub for client RMI methods. This will be passed to the
     * server so that the server can call our receiveMessage().
     */
    private void makeClientStub(IClientDisplay cd) throws RemoteException {
        clientStub = (IChatClientRMI) new IChatClientRMIImpl(cd);
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="GUI Building Code">
    private void buildGUI() {
        //setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        // Create a menu bar and tell MainForm to use it:
        mnuBarMain = buildMenu();
        this.setJMenuBar(mnuBarMain);

        // Find the Container on which we'll put all other components:
        Container pane = this.getContentPane();
        pane.setLayout(new BorderLayout());
        //mnuBarMain = buildMenu();
        //pane.add(mnuBarMain);
        // Build top panel and add it to pane:
        pnlHeader = buildHeader();
        pane.add(pnlHeader, BorderLayout.PAGE_START);
        // Now the main (central) panel:
        pnlMain = buildMainPanel();
        pane.add(pnlMain, BorderLayout.CENTER);
        // And the bottom one:
        pnlBottom = buildBottomPanel();
        pane.add(pnlBottom, BorderLayout.PAGE_END);
        // Initialize fields and labels:
        this.initStrings();
        // Construct strings to be shown in status
        // label, depending on logged-in/logged-out state:
        this.buildLoginStrings();
        // Initialize window size and position:
        this.setGeometry();
    }

    /**
     * Utility methods to create menu items and associate them with their event
     * handlers.
     *
     *
     * @return
     */
    private JMenuItem makeFileLoginItem() {
        String menuText = this.prefStore.getFileLoginMenuText();
        JMenuItem item = new JMenuItem(menuText, KeyEvent.VK_I);
        class FileLoginActionListener implements ActionListener {

            public void actionPerformed(ActionEvent evt) {
                MainForm.this.login();
            }
        }

        item.addActionListener(new FileLoginActionListener());
        return item;
    }

    private JMenuItem makeFileLogoutItem() {
        String menuText = this.prefStore.getFileLogoutMenuText();
        JMenuItem item = new JMenuItem(menuText, KeyEvent.VK_O);
        class FileLogoutActionListener implements ActionListener {

            public void actionPerformed(ActionEvent evt) {
                MainForm.this.logout();
            }
        }

        item.addActionListener(new FileLogoutActionListener());
        return item;
    }

    private JMenuItem makeFileExitItem() {
        String menuText = this.prefStore.getFileExitMenuText();
        JMenuItem item = new JMenuItem(menuText);
        class FileExitActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                //MainForm.this.saveConfig();
                //MainForm.this.dispose();
                //JOptionPane.showMessageDialog(MainForm.this, "Exit Menu Clicked");
                doExit(0);
            }
        }

        item.addActionListener(new FileExitActionListener());
        return item;
    }

    private JMenuItem makeEditOptionsItem() {
        String menuText = this.prefStore.getEditOptionsMenuText();
        JMenuItem item = new JMenuItem(menuText, KeyEvent.VK_P);
        class EditOptionsActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(MainForm.this,
                        "Sorry -- Options functions not yet implemented",
                        "Options",
                        JOptionPane.INFORMATION_MESSAGE);
            }
        }

        item.addActionListener(new EditOptionsActionListener());
        return item;
    }

    private JMenuBar buildMenu() {
        // File menu and menu items:
        mnuBarMain = new JMenuBar();
        mnuFile = new JMenu();
        String menuText = this.prefStore.getFileMenuText();
        mnuFile.setText(menuText);
        mnuFile.setMnemonic(KeyEvent.VK_F);
        mniLogin = makeFileLoginItem();
        mnuFile.add(mniLogin);
        mniLogout = makeFileLogoutItem();
        mnuFile.add(mniLogout);
        mnuFile.add(new JSeparator());
        mniExit = this.makeFileExitItem();
        mnuFile.add(mniExit);
        mnuBarMain.add(mnuFile);

        // Edit menu and menu item:
        mnuEdit = new JMenu();
        menuText = this.prefStore.getEditMenuText();
        mnuEdit.setText(menuText);
        mnuEdit.setMnemonic(KeyEvent.VK_E);
        mniOptions = makeEditOptionsItem();
        mnuEdit.add(mniOptions);
        mnuBarMain.add(mnuEdit);

        return mnuBarMain;
    }

    /**
     * Create component (only one in this case) to fill top panel and return
     * JPanel object which will later be added to main JFrame content pane.
     *
     * @return JPanel
     */
    private JPanel buildHeader() {
        // Make a panel which we'll return:
        JPanel pnl = new JPanel(new BorderLayout());
        // Sub-panel which will hold top status label on left
        // and login button on right:
        JPanel subPanel = new JPanel(new BorderLayout());
        // Add status label to sub-panel:
        this.lblTopStatus = new JLabel(this.prefStore.getLoggedOutStatusLabel());
        subPanel.add(lblTopStatus, BorderLayout.LINE_START);
        // and Friends List button:
        this.btnFriendsList = this.makeFriendsListButton();
        subPanel.add(this.btnFriendsList, BorderLayout.CENTER);
        // and login button:
        this.btnLogin = this.makeLoginButton();
        subPanel.add(this.btnLogin, BorderLayout.LINE_END);
        // Add sub-panel to panel which we'll return:
        pnl.add(subPanel, BorderLayout.CENTER);
        return pnl;
    }

    /**
     * Assemble main (center) panel. This holds a JScrollPane, which in turn
     * holds the JListClientDisplay
     *
     * @return JPanel
     */
    private JPanel buildMainPanel() {
        JPanel pnl = new JPanel(new BorderLayout());
        JScrollPane scrMain = new JScrollPane();
        //pnl.add(scrMain, BorderLayout.CENTER);
        pnl.add(scrMain);

        clientDisplay = new JListClientDisplay();
        clientDisplay.initMessageDisplay();
        //scrMain.add(clientDisplay, BorderLayout.CENTER);
        scrMain.add(clientDisplay);
        scrMain.setViewportView(clientDisplay);
        return pnl;
    }

    /**
     * Putting the exception handling here instead of in doSendButtonAction()
     * means if sending fails, text field is already cleared -- user will have
     * to re-type message to re-try.
     *
     * @return
     */
    private JButton makeSendButton() {
        JButton btn = new JButton(this.prefStore.getSendButtonText());
        class SendButtonActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    doSendButtonAction();
                } catch (RemoteException ex) {
                    JOptionPane.showMessageDialog(
                            rootPane,
                            prefStore.getSendingFailureMessage(),
                            "",
                            JOptionPane.ERROR_MESSAGE);
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        btn.addActionListener(new SendButtonActionListener());
        return btn;
    }

    private JButton makeBuyAdsButton() {
        JButton btn = new JButton(this.prefStore.getBuyAdsButtonText());
        class BuyAdsButtonActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    doBuyAdsButtonAction();
                } catch (RemoteException ex) {
                    JOptionPane.showMessageDialog(
                            rootPane,
                            "Failure in call to Buy Ads",
                            "",
                            JOptionPane.ERROR_MESSAGE);
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        btn.addActionListener(new BuyAdsButtonActionListener());
        return btn;
    } // end makeBuyAdsButton()

    private JButton makeFriendsListButton() {
        JButton btn = new JButton(this.prefStore.getFriendsListButtonText());
        class FriendsListButtonActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    doFriendsListButtonAction();
                } catch (RemoteException ex) {
                    JOptionPane.showMessageDialog(
                            rootPane,
                            "Failure in call to Launch Friends List",
                            "",
                            JOptionPane.ERROR_MESSAGE);
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        btn.addActionListener(new FriendsListButtonActionListener());
        return btn;
    } // end makeFriendsListButton()

    private JButton makeLoginButton() {
        JButton btn = new JButton(this.prefStore.getFileLoginMenuText());

        class LoginButtonActionListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (MainForm.this.isLoggedIn) {
                    MainForm.this.logout();
                } else {
                    MainForm.this.login();
                }
            }
        }

        btn.addActionListener(new LoginButtonActionListener());
        return btn;
    } // end makeLoginButton()

    /**
     * Assemble bottom panel, containing three components: 1. pane containing
     * send button and hint 2. text entry field for typing message, and 3. label
     * for displaying ads
     *
     * @return
     */
    private JPanel buildBottomPanel() {
        JPanel pnl = new JPanel(new BorderLayout());
        Container paneSend = new Container();
        paneSend.setLayout(new BorderLayout());
        this.lblSendHint = new JLabel(this.prefStore.getSendHint());
        paneSend.add(lblSendHint, BorderLayout.LINE_START);
        this.btnSend = makeSendButton();
        // Make this button the default -- ie, it will
        // be "clicked" when user presses Enter:
        this.getRootPane().setDefaultButton(btnSend);

        paneSend.add(btnSend, BorderLayout.LINE_END);
        pnl.add(paneSend, BorderLayout.PAGE_START);
        this.txtComposeMessage = new JTextField(this.prefStore.getSendHint());
        pnl.add(this.txtComposeMessage, BorderLayout.CENTER);

        // Add a sub-panel which will hold ad text and the
        // Buy Ads button:
        JPanel subPanel = new JPanel(new BorderLayout());
        // Add the ad display label on left end of sub-panel:
        this.lblAdDisplay = new JLabel("Ads go here");
        subPanel.add(lblAdDisplay, BorderLayout.LINE_START);
        // Now the button:
        this.btnBuyAds = this.makeBuyAdsButton();
        subPanel.add(btnBuyAds, BorderLayout.LINE_END);
        // Add sub-panel to panel:
        pnl.add(subPanel, BorderLayout.PAGE_END);
        return pnl;
    }

    /**
     * Set various text labels that are known at construction time.
     */
    private void initStrings() {
        // button
        this.btnSend.setText(prefStore.getSendButtonText());
        // labels
        this.lblSendHint.setText(prefStore.getSendHint());
        this.lblTopStatus.setText(prefStore.getLoggedOutStatusLabel());

        // message display format and initial text
        this.clientDisplay.setTimeFormat(prefStore.getTimeFormat());
        this.clientDisplay.setusernameFormat(prefStore.getUsernameFormat());
        // app title
        this.setTitle(prefStore.getAppTitle());
    }

    /**
     * Try to read size and position from prefStore
     */
    private void setGeometry() {
        this.setSize(prefStore.getWindowWidth(),
                prefStore.getWindowHeight());
        this.setLocation(prefStore.getWindowLeft(),
                prefStore.getWindowTop());
    } // end setGeometry()

    // </editor-fold>
    private void doSendButtonAction() throws RemoteException {
        if (this.isLoggedIn) {
            if (this.txtComposeMessage.getText() != null) {
                String outMessage =
                        this.txtComposeMessage.getText().trim();
                if (!outMessage.isEmpty()) {
                    // Construct a Message to send
                    Message msg = new Message();
                    msg.setContent(outMessage);
                    msg.setSender(loggedInAs);
                    // ignore recipients field for now
                    msg.setRecipients(null);
                    // Send the message:
                    this.serverStub.sendMessage(
                            msg, msg.getSender().getUserName());
                    // Clear the input field and refocus on it:
                    this.txtComposeMessage.setText("");
                    this.txtComposeMessage.requestFocusInWindow();
                } // end if (not empty)
            } // end if (text != null)

        } // end if txtComposeMessage.getText() != null
        else { // not logged in
            JOptionPane.showMessageDialog(rootPane,
                    prefStore.getMustLogInReminder(),
                    prefStore.getMustLogInReminderTitle(),
                    JOptionPane.ERROR_MESSAGE);
        }
    } // end doSendButtonAction()

    void doBuyAdsButtonAction() throws RemoteException {
        AdGui adGuiStarter = new AdGui();
        adGuiStarter.setVisible(true);
        btnBuyAds.setVisible(false);
    }

    void doFriendsListButtonAction() throws RemoteException {
        FriendsList fl;
        try {
            fl = new FriendsList();
            fl.setVisible(true);
        } catch (NotBoundException ex) {
            Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //<editor-fold defaultstate="collapsed" desc="Login and Logout Code">
    private void logout() {
        if (isLoggedIn) {
            try {
                ResultCodes logoutResult = serverStub.logout(loggedInAs.getUserName());
                if (logoutResult != ResultCodes.LOGOUT_OK) {
                    JOptionPane.showMessageDialog(rootPane,
                            "Error logging out: "
                            + logoutResult.toString(),
                            "Error logging out",
                            JOptionPane.ERROR_MESSAGE);
                } else {
                    this.setStateToLoggedOut();
                }
            } catch (RemoteException ex) {
                JOptionPane.showMessageDialog(rootPane,
                        "Could not call server's logout method",
                        "RMI Failure",
                        JOptionPane.ERROR_MESSAGE);
                Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    } // end logout()

    /**
     * Return user name. If error or cancel, return null
     *
     * @return
     */
    private String promptForUsername() {
        String ret = JOptionPane.showInputDialog(rootPane,
                prefStore.getUsernamePrompt(),
                prefStore.getLoginTitle(),
                JOptionPane.PLAIN_MESSAGE);
        return (ret == null) ? ret : ret.trim();
    }

    private String promptForPassword() {
        String ret = JOptionPane.showInputDialog(rootPane,
                prefStore.getPasswordPrompt(),
                prefStore.getLoginTitle(),
                JOptionPane.PLAIN_MESSAGE);
        return (ret == null) ? ret : ret.trim();
    }

    private ResultCodes loginInternal(String username, String pword) {
        ResultCodes loginResult;
        if (!isLoggedIn) {
            try {
                loginResult = serverStub.login(
                        username,
                        pword,
                        clientStub);
            } catch (RemoteException ex) {
                Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                loginResult = ResultCodes.UNKNOWN_LOGIN_ERROR;
            }
        } // if ! logged in
        else {
            loginResult = ResultCodes.ALREADY_LOGGED_IN;
        }
        return loginResult;
    }

    private void login() {
        if (!isLoggedIn) {
            String uname = null;
            String pword = null;
            uname = promptForUsername();
            if (uname != null
                    && !uname.isEmpty()) {
                pword = promptForPassword();
            }
            if (pword != null
                    && !pword.isEmpty()) {
                ResultCodes loginResult = loginInternal(uname, pword);
                if (loginResult == ResultCodes.LOGIN_OK
                        || loginResult == ResultCodes.ALREADY_LOGGED_IN) {
                    setStateToLoggedIn(uname);
                } // end if logged in OK
                else { // not logged in OK
                    JOptionPane.showMessageDialog(rootPane,
                            prefStore.getLoginErrorMsg(),
                            loginResult.toString(),
                            JOptionPane.ERROR_MESSAGE);
                }
            } // password entered
        } // end if not logged in
        else {
            JOptionPane.showMessageDialog(rootPane,
                    prefStore.getAlreadyLoggedInMsg(),
                    prefStore.getAlreadyLoggedInTitle(),
                    JOptionPane.INFORMATION_MESSAGE);
        }
    } // end login()

    //        </editor-fold>
    // TODO: Put in here code to gracefully
    // shut down threads that have been started
    // by the MainForm.
    //
    private void saveConfig() {
        JOptionPane.showMessageDialog(rootPane,
                "Saving Config",
                "Saving Config",
                JOptionPane.INFORMATION_MESSAGE);
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

    //<editor-fold defaultstate="collapsed" desc="WindowListener Code">
    @Override
    public void windowOpened(WindowEvent e) {
    }

    @Override
    public void windowClosing(WindowEvent e) {
        //JOptionPane.showMessageDialog(this, "windowClosing Window Event");
        doExit(0);
    }

    @Override
    public void windowClosed(WindowEvent e) {
    }

    @Override
    public void windowIconified(WindowEvent e) {
    }

    @Override
    public void windowDeiconified(WindowEvent e) {
    }

    @Override
    public void windowActivated(WindowEvent e) {
    }

    @Override
    public void windowDeactivated(WindowEvent e) {
    }
    //</editor-fold>

    /**
     * Put stuff in here that's to be done when the application is exiting.
     */
    private static void doExit(int exitStatus) {
        //JOptionPane.showMessageDialog(null, "In doExit()");
        //listener.setAllDone(true);
        //JOptionPane.showMessageDialog(rootPane,
        //        "Listener thread should be wrapping up");
        System.exit(exitStatus);
    }

    /**
     * Construct the strings which will be shown in the status line. There are
     * different strings depending on logged-in vs. logged-out status.
     */
    private void buildLoginStrings() {
        // Construct loggedInStatusString:
        StringBuilder sb = new StringBuilder();
        sb.append(prefStore.getConnectedMsg());
        sb.append(" ");
        sb.append(serverIP);
        sb.append(" ");
        sb.append(prefStore.getLoggedInStatusLabel());
        sb.append(" | ");
        loggedInStatusString = sb.toString();
        // clear sb and use it to construct loggedOutStatusString:
        sb.delete(0, sb.length());
        sb.append(prefStore.getConnectedMsg());
        sb.append(" ");
        sb.append(serverIP);
        sb.append(" | ");
        sb.append(prefStore.getLoggedOutStatusLabel());
        loggedOutStatusString = sb.toString();
    }

    /**
     * Inner class which implements IChatClientRMI
     */
    private class IChatClientRMIImpl extends UnicastRemoteObject implements IChatClientRMI {

        IClientDisplay clientDisplay;
        PrefStore prefStore;

        public IChatClientRMIImpl(IClientDisplay cd) throws RemoteException {
            super();
            clientDisplay = cd;
            // Make ourselves a local copy
            // of preferences/configuration store:
            prefStore = PrefStore.makePrefStore();
        }

        @Override
        public void receiveMessage(Message msg) throws RemoteException {
            clientDisplay.appendMessage(msg);
        }

        @Override
        public PrivacySettings getPrivacySetting() throws RemoteException {
            return prefStore.getPrivacy();
        }
    } // end of Class IChatClientRMIImpl   

    public class AdUpdate implements Runnable {

        Thread adUpdate;

        @Override
        public void run() {
            //If I have coded this right this thread once started should create a null string,
            //use the push ads method to populate it, change the lblDisplay to that ad, and sleep for two minutes.
            int i = 0;
            while (i < 1) {
                String retrievedAD = null;
                try {
                    retrievedAD = advertisingObj.pushAds();
                } 
                catch (RemoteException ex) {
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
                lblAdDisplay.setText(retrievedAD);
                try {
                    Thread.sleep(120000);
                } 
                catch (InterruptedException ex) {
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
} // end Class MainForm
